<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <p data-lake-id="ufaee4df9" id="ufaee4df9"><br></p>
  <p data-lake-id="uf664aca3" id="uf664aca3"><span data-lake-id="ud49d1b29" id="ud49d1b29">涉及到分库分表，就会引申出分布式系统中唯一主键ID的生成问题，因为在单表中我们可以用数据库主键来做唯一ID，但是如果做了分库分表，多张单表中的自增主键就一定会发生冲突。那就不具备全局唯一性了。</span></p>
  <p data-lake-id="u4ee6df01" id="u4ee6df01"><br></p>
  <p data-lake-id="u6d4a05d2" id="u6d4a05d2"><span data-lake-id="u1d23e1ef" id="u1d23e1ef">那么，如何生成一个全局唯一的ID呢？有以下几种方式：</span></p>
  <p data-lake-id="uec42ea20" id="uec42ea20"><br></p>
  <h3 data-lake-id="Nk0Rf" id="Nk0Rf"><span data-lake-id="u273d19a9" id="u273d19a9">UUID</span></h3>
  <p data-lake-id="ub9073563" id="ub9073563"><br></p>
  <p data-lake-id="u8c566795" id="u8c566795"><span data-lake-id="u5d0f1398" id="u5d0f1398">很多人对UUID都不陌生，它是可以做到全局唯一的，而且生成方式也简单，但是我们通常不推荐使用他做唯一ID，首先</span><strong><span data-lake-id="u5bca108a" id="u5bca108a">UUID太长了，其次字符串的查询效率也比较慢，而且没有业务含义，根本看不懂。</span></strong></p>
  <p data-lake-id="ubd1e4987" id="ubd1e4987"><br></p>
  <h3 data-lake-id="rkUM2" id="rkUM2"><span data-lake-id="ub13eb72e" id="ub13eb72e">基于某个单表做自增主键</span></h3>
  <p data-lake-id="ubd14b750" id="ubd14b750"><br></p>
  <p data-lake-id="u8621ef4a" id="u8621ef4a"><span data-lake-id="u943523ba" id="u943523ba">多张单表生成的自增主键会冲突，但是如果所有的表中的主键都从同一张表生成是不是就可以了。</span></p>
  <p data-lake-id="u89067591" id="u89067591"><br></p>
  <p data-lake-id="ucdd775cb" id="ucdd775cb"><span data-lake-id="ucd09d9f9" id="ucd09d9f9">所有的表在需要主键的时候，都到这张表中获取一个自增的ID。</span></p>
  <p data-lake-id="ub640202f" id="ub640202f"><br></p>
  <p data-lake-id="u55d55eab" id="u55d55eab"><span data-lake-id="u16a6cd2e" id="u16a6cd2e">这样做是可以做到唯一，也能实现自增，但是问题是这个单表就变成整个系统的瓶颈，而且也存在单点问题，一旦他挂了，那整个数据库就都无法写入了。</span></p>
  <p data-lake-id="uf6088148" id="uf6088148"><br></p>
  <h3 data-lake-id="LuSuB" id="LuSuB"><span data-lake-id="uc478e6db" id="uc478e6db">基于多个单表+步长做自增主键</span></h3>
  <p data-lake-id="ud7581141" id="ud7581141"><br></p>
  <p data-lake-id="u20758fb6" id="u20758fb6"><span data-lake-id="ue7a6712f" id="ue7a6712f">为了解决单个数据库做自增主键的瓶颈及单点故障问题，我们可以引入多个表来一起生成就行了。</span></p>
  <p data-lake-id="uacdffd82" id="uacdffd82"><br></p>
  <p data-lake-id="ua409ade5" id="ua409ade5"><span data-lake-id="u550be986" id="u550be986">但是如何保证多张表里面生成的Id不重复呢？如果我们能实现以下的生成方式就行了：</span></p>
  <p data-lake-id="u97ded5fe" id="u97ded5fe"><br></p>
  <p data-lake-id="u6ff1ac61" id="u6ff1ac61"><span data-lake-id="u694a26ec" id="u694a26ec">实例1生成的ID从1000开始，到1999结束。 实例2生成的ID从2000开始，到2999结束。 实例3生成的ID从3000开始，到3999结束。 实例4生成的ID从4000开始，到4999结束。</span></p>
  <p data-lake-id="u85fc9ea0" id="u85fc9ea0"><br></p>
  <p data-lake-id="ub2716d55" id="ub2716d55"><img src="https://cdn.nlark.com/yuque/0/2023/jpeg/5378072/1673157793847-71d290f7-9c3e-473c-a60b-40ec935ec9ad.jpeg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_22%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="ue7c278b1" id="ue7c278b1"><br></p>
  <p data-lake-id="u2a5d98f9" id="u2a5d98f9"><span data-lake-id="ub7840717" id="ub7840717">这样就能避免ID重复了，那如果第一个实例的ID已经用到1999了怎么办？那就生成一个新的起始值：</span></p>
  <p data-lake-id="uf1681cd1" id="uf1681cd1"><br></p>
  <p data-lake-id="uf2aee345" id="uf2aee345"><span data-lake-id="u9557f0a7" id="u9557f0a7">实例1生成的ID从5000开始，到5999结束。 实例2生成的ID从6000开始，到6999结束。 实例3生成的ID从7000开始，到7999结束。 实例4生成的ID从8000开始，到8999结束。</span></p>
  <p data-lake-id="ue8fe9ebe" id="ue8fe9ebe"><br></p>
  <p data-lake-id="u15ed0cf8" id="u15ed0cf8"><span data-lake-id="u607bddce" id="u607bddce">我们把步长设置为1000，确保每一个单表中的主键起始值都不一样，并且比当前的最大值相差1000就行了。</span></p>
  <p data-lake-id="u2efe7cdf" id="u2efe7cdf"><br></p>
  <h3 data-lake-id="BiDuV" id="BiDuV"><span data-lake-id="uc5cf56ff" id="uc5cf56ff">雪花算法</span></h3>
  <p data-lake-id="ue45d3349" id="ue45d3349"><br></p>
  <p data-lake-id="u2c64e4fe" id="u2c64e4fe"><strong><span data-lake-id="u93fdc142" id="u93fdc142">雪花算法也是比较常用的一种分布式ID的生成方式，它具有全局唯一、递增、高可用的特点。</span></strong></p>
  <p data-lake-id="uac292da1" id="uac292da1"><br></p>
  <p data-lake-id="u50d1d8e0" id="u50d1d8e0"><span data-lake-id="uedb8b22e" id="uedb8b22e">雪花算法生成的主键主要由 4 部分组成，1bit符号位、41bit时间戳位、10bit工作进程位以及 12bit 序列号位。</span></p>
  <p data-lake-id="u2e07ff86" id="u2e07ff86"><br></p>
  <p data-lake-id="u7a061d41" id="u7a061d41"><span data-lake-id="ub21a8c7e" id="ub21a8c7e">时间戳占用41bit，精确到毫秒，总共可以容纳约69年的时间。</span></p>
  <p data-lake-id="ue0d31519" id="ue0d31519"><br></p>
  <p data-lake-id="u91236691" id="u91236691"><span data-lake-id="u9a5076e0" id="u9a5076e0">工作进程位占用10bit，其中高位5bit是数据中心ID，低位5bit是工作节点ID，做多可以容纳1024个节点。</span></p>
  <p data-lake-id="ub61e22e9" id="ub61e22e9"><br></p>
  <p data-lake-id="u05ff5541" id="u05ff5541"><span data-lake-id="u86f6c4a8" id="u86f6c4a8">序列号占用12bit，每个节点每毫秒0开始不断累加，最多可以累加到4095，一共可以产生4096个ID。</span></p>
  <p data-lake-id="ua3a04b36" id="ua3a04b36"><br></p>
  <p data-lake-id="uefdfd5fe" id="uefdfd5fe"><span data-lake-id="uc0c0d660" id="uc0c0d660">所以，一个雪花算法可以在同一毫秒内最多可以生成1024 X 4096 = 4194304个唯一的ID</span></p>
  <p data-lake-id="ua84067ee" id="ua84067ee"><span data-lake-id="u6eb997a2" id="u6eb997a2">​</span><br></p>
 </body>
</html>